﻿using RedLockNet;
using RedLockNet.SERedis;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Tsual.DataPipeline.Redis
{
    public abstract class AbstractRedisPipe<T, TMeta> : IDataPipeDescription<T, TMeta>, IDisposable
        where TMeta : IConvertible<LockType>
    {
        protected IDatabase cache { get; }
        protected RedLockFactory lockFactory { get; }
        protected string ExpHead = "EXP:";
        protected AbstractRedisPipe(IDatabase cache, RedLockFactory lockFactory)
        {
            this.cache = cache ?? throw new ArgumentNullException(nameof(cache));
            this.lockFactory = lockFactory ?? throw new ArgumentNullException(nameof(lockFactory));
        }

        public abstract bool CanRead(TMeta meta);
        public abstract bool CanWrite(T data, TMeta meta);
        public abstract Task<DataPipeReadResult<T>> ReadCache(TMeta meta);
        public abstract Task<DataPipeWriteResult> WriteCache(T data, TMeta meta);
        protected abstract string GetReadLockKey(TMeta meta);
        protected virtual string GetWriteLockKey(T data, TMeta meta) => GetReadLockKey(meta);

        private IRedLock readlock;
        private IRedLock writelock;

        public Task<DataPipeReadResult<T>> Read(TMeta meta)
        {
            var ls = meta.Convert();
            if (ls == LockType.Lock || ls == LockType.Read)
            {
                readlock = lockFactory.CreateLock(GetReadLockKey(meta), TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15), TimeSpan.FromMilliseconds(100));
                if (readlock.IsAcquired)
                {
                    return ReadCache(meta);
                }
                else
                {
                    return Task.Run(() => DataPipeReadResult<T>.Error(new TimeoutException()));
                }
            }
            else
            {
                return ReadCache(meta);
            }


            throw new NotImplementedException();
        }

        public Task<DataPipeWriteResult> Write(T data, TMeta meta)
        {
            var ls = meta.Convert();
            if (ls == LockType.Lock || ls == LockType.Write)
            {
                var wls = GetWriteLockKey(data, meta);
                if (readlock != null && readlock.Resource != wls)
                {
                    writelock = lockFactory.CreateLock(wls, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(100));
                    if (writelock.IsAcquired)
                    {
                        return WriteCache(data, meta);
                    }
                    else
                    {
                        return Task.Run(() => DataPipeWriteResult.Error(new TimeoutException()));
                    }
                }
                else
                {
                    return WriteCache(data, meta);
                }
                
            }
            else
            {
                return WriteCache(data, meta);
            }


            throw new NotImplementedException();
        }

        void IDisposable.Dispose()
        {
            readlock?.Dispose();
            writelock?.Dispose();
        }
    }

    public enum LockType
    {
        Normal,
        Write,
        Read,
        Lock,
    }
}
